﻿using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using Bricks;
using Bricks.Logging;
using Microsoft.Practices.Prism;
using Microsoft.Practices.Prism.Regions;
using Microsoft.Practices.Prism.Regions.Behaviors;
using Microsoft.Practices.ServiceLocation;
using Motar.Desktop.Properties;

namespace Motar.Desktop
{

    /// <summary>
    /// 
    /// </summary>
    public class DesktopInitializer<TShell> : ApplicationInitializerBase where TShell : Window
    {

        /// <summary>
        /// 
        /// </summary>
        private readonly ILoggerFacade _loggerFacade;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="containerFacade"></param>
        public DesktopInitializer(Bricks.Injecting.IContainerFacade containerFacade, ILoggerFacade loggerFacade) :
            base(containerFacade)
        {
            if (loggerFacade == null)
            {
                throw new ArgumentNullException("loggerFacade");
            }
            _loggerFacade = loggerFacade;
        }

        /// <summary>
        /// 
        /// </summary>
        public ILoggerFacade LoggerFacade
        {
            get { return _loggerFacade; }
        }

        /// <summary>
        /// 
        /// </summary>
        public override void Execute()
        {
            LoggerFacade.Debug(Resources.ConfiguringServiceLocatorSingleton);
            ConfigureServiceLocator();

            LoggerFacade.Debug(Resources.ConfiguringRegionAdapters);
            ConfigureRegionAdapterMappings();

            LoggerFacade.Debug(Resources.ConfiguringDefaultRegionBehaviors);
            ConfigureDefaultRegionBehaviors();

            LoggerFacade.Debug(Resources.RegisteringFrameworkExceptionTypes);
            RegisterFrameworkExceptionTypes();

            InitializeShell();
        }

        /// <summary>
        /// 
        /// </summary>
        protected virtual void ConfigureServiceLocator()
        {
            Execute(containerFacade => ServiceLocator.SetLocatorProvider(() => containerFacade.Resolve<IServiceLocator>()));
        }

        /// <summary>
        /// 
        /// </summary>
        protected virtual RegionAdapterMappings ConfigureRegionAdapterMappings()
        {
            return Execute<RegionAdapterMappings>(
                containerFacade =>
                {
                    RegionAdapterMappings regionAdapterMappings = null;

                    if (containerFacade.TryResolve<RegionAdapterMappings>(out regionAdapterMappings))
                    {
                        regionAdapterMappings.RegisterMapping(typeof(Selector), ServiceLocator.Current.GetInstance<SelectorRegionAdapter>());

                        regionAdapterMappings.RegisterMapping(typeof(ItemsControl), ServiceLocator.Current.GetInstance<ItemsControlRegionAdapter>());

                        regionAdapterMappings.RegisterMapping(typeof(ContentControl), ServiceLocator.Current.GetInstance<ContentControlRegionAdapter>());
                    }

                    return regionAdapterMappings;
                });
        }

        /// <summary>
        /// 
        /// </summary>
        protected virtual IRegionBehaviorFactory ConfigureDefaultRegionBehaviors()
        {
            return Execute<IRegionBehaviorFactory>(
                containerFacade =>
                {
                    IRegionBehaviorFactory defaultRegionBehaviorTypesDictionary = null;

                    if (containerFacade.TryResolve<IRegionBehaviorFactory>(out defaultRegionBehaviorTypesDictionary))
                    {
                        defaultRegionBehaviorTypesDictionary.AddIfMissing(AutoPopulateRegionBehavior.BehaviorKey, typeof(AutoPopulateRegionBehavior));

                        defaultRegionBehaviorTypesDictionary.AddIfMissing(BindRegionContextToDependencyObjectBehavior.BehaviorKey, typeof(BindRegionContextToDependencyObjectBehavior));

                        defaultRegionBehaviorTypesDictionary.AddIfMissing(RegionActiveAwareBehavior.BehaviorKey, typeof(RegionActiveAwareBehavior));

                        defaultRegionBehaviorTypesDictionary.AddIfMissing(SyncRegionContextWithHostBehavior.BehaviorKey, typeof(SyncRegionContextWithHostBehavior));

                        defaultRegionBehaviorTypesDictionary.AddIfMissing(RegionManagerRegistrationBehavior.BehaviorKey, typeof(RegionManagerRegistrationBehavior));

                        defaultRegionBehaviorTypesDictionary.AddIfMissing(RegionMemberLifetimeBehavior.BehaviorKey, typeof(RegionMemberLifetimeBehavior));

                        defaultRegionBehaviorTypesDictionary.AddIfMissing(ClearChildViewsRegionBehavior.BehaviorKey, typeof(ClearChildViewsRegionBehavior));
                    }

                    return defaultRegionBehaviorTypesDictionary;
                });
        }

        /// <summary>
        /// 
        /// </summary>
        protected virtual void RegisterFrameworkExceptionTypes()
        {
            ExceptionExtensions.RegisterFrameworkExceptionType(typeof(Microsoft.Practices.ServiceLocation.ActivationException));
        }

        /// <summary>
        /// 
        /// </summary>
        protected virtual TShell InitializeShell()
        {
            return Execute<TShell>(
                containerFacade =>
                {
                    TShell shell = null;

                    LoggerFacade.Debug(Resources.CreatingShell);
                    if (containerFacade.TryResolve<TShell>(out shell))
                    {
                        LoggerFacade.Debug(Resources.SettingTheRegionManager);
                        RegionManager.SetRegionManager(shell.As<DependencyObject>(), containerFacade.Resolve<IRegionManager>());

                        LoggerFacade.Debug(Resources.UpdatingRegions);
                        RegionManager.UpdateRegions();

                        LoggerFacade.Debug(Resources.InitializingShell);
                        Application.Current.MainWindow = shell;

                        Application.Current.MainWindow.Show();
                    }

                    return shell;
                });
        }
    }
}
